#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>

#include <pthread.h>


int fd;
FILE *file;
char *file_buffer;
unsigned long fileLen;
char *dev_name;
char *file_name;

unsigned char cur_checksum = 0;

unsigned char read_finish = 0;
unsigned char resend_flag = 0;
unsigned char exit_flag = 0;

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
	struct termios newtio,oldtio;
	if  ( tcgetattr( fd,&oldtio)  !=  0) 
	{ 
		perror("SetupSerial 1");
		return -1;
	}
	bzero( &newtio, sizeof( newtio ) );
	newtio.c_cflag  |=  CLOCAL | CREAD; 
	newtio.c_cflag &= ~CSIZE; 

	switch( nBits )
	{
		case 7:
			newtio.c_cflag |= CS7;
			break;
		case 8:
			newtio.c_cflag |= CS8;
			break;
	}

	switch( nEvent )
	{
		case 'O':                     //奇校验
			newtio.c_cflag |= PARENB;
			newtio.c_cflag |= PARODD;
			newtio.c_iflag |= (INPCK | ISTRIP);
			break;
		case 'E':                     //偶校验
			newtio.c_iflag |= (INPCK | ISTRIP);
			newtio.c_cflag |= PARENB;
			newtio.c_cflag &= ~PARODD;
			break;
		case 'N':                    //无校验
			newtio.c_cflag &= ~PARENB;
			break;
	}

	switch( nSpeed )
	{
		case 2400:
			cfsetispeed(&newtio, B2400);
			cfsetospeed(&newtio, B2400);
			break;
		case 4800:
			cfsetispeed(&newtio, B4800);
			cfsetospeed(&newtio, B4800);
			break;
		case 9600:
			cfsetispeed(&newtio, B9600);
			cfsetospeed(&newtio, B9600);
			break;
		case 115200:
			cfsetispeed(&newtio, B115200);
			cfsetospeed(&newtio, B115200);
			break;
		default:
			cfsetispeed(&newtio, B9600);
			cfsetospeed(&newtio, B9600);
			break;
	}

	if( nStop == 1 )
	{
		newtio.c_cflag &=  ~CSTOPB;
	}
	else if ( nStop == 2 )
	{
		newtio.c_cflag |=  CSTOPB;
	}
	newtio.c_cc[VTIME]  = 0;
	newtio.c_cc[VMIN] = 0;
	tcflush(fd,TCIFLUSH);
	if((tcsetattr(fd,TCSANOW,&newtio))!=0)
	{
		perror("com set error");
		return -1;
	}
	printf("set done!\n");
	return 0;
}

int open_port(int fd)
{
	fd = open( dev_name, O_RDWR|O_NOCTTY|O_NDELAY);
	if (-1 == fd)
	{
		perror("Can't Open Serial Port");
		return(-1);
	}
	else 
	{
		printf("open %s .....\n",dev_name);
	}

    if(fcntl(fd, F_SETFL, 0)<0)
    {
        printf("fcntl failed!\n");
    }
    else
    {
        printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));
    }
    if(isatty(STDIN_FILENO)==0)
    {
        printf("standard input is not a terminal device\n");
    }
    else
    {
        printf("isatty success!\n");
    }
    printf("fd-open=%d\n",fd);
    return fd;
}

unsigned char checkSumFun(unsigned char *dst, int len)
{
    unsigned char sum = 0;
    int i = 0;

    for (i = 0; i < len; i++)
    {
        sum = (unsigned char)((sum + dst[i]) & 0xFF);
    }

    return sum;
}

#if 1
void hexStr_to_u8(const char *hexStr,unsigned char *u8data,int u8data_len)
{
	int i;
	unsigned char highByte, lowByte;

	for (i = 0; i < u8data_len; i ++)
	{
		highByte = hexStr[2*i];
		lowByte = hexStr[2*i + 1];

		if(highByte > 0x60)
			highByte -= 0x57;
		else if (highByte > 0x39)
			highByte -= 0x37;
		else
			highByte -= 0x30;

		if(lowByte > 0x60)
			lowByte -= 0x57;
		else if (lowByte > 0x39)
			lowByte -= 0x37;
		else
			lowByte -= 0x30;

		u8data[i] = (highByte << 4) | lowByte;
	}
}
#endif

void *read_thread(void *arg)
{
	unsigned char *buff = NULL;
	int nread = 0;
	unsigned char checksum = 0;
	printf("[%s] running, Thread ID = %lu\n",__func__, pthread_self());

	buff = (char *)malloc(512);
	if(buff == NULL)
	{
		printf("malloc failed \n");
		return NULL;
	}
	else
	{
		memset(buff,0,512);
	}

	while(1)
	{
		if(strlen(buff)>0)
		{
			memset(buff,0,512);
		}

		nread = read(fd,buff,512);
		if( nread > 0 )
		{
			//目前收字符串，如果是十六进制则不用转换
			//hexStr_to_u8(buff,&checksum,strlen(buff));
			checksum = buff[0];
			printf("nread=%d,src=[%s] cur_checksum=%02x checksum=%02x\n",nread,buff,cur_checksum,checksum);
			if(cur_checksum == checksum)
			{
				read_finish = 1;
			}
			else
			{
				resend_flag = 1;
			}
		}
		usleep(1000);
	}
	return NULL;
}

#define FRAME_LEN 65

void *write_thread(void *arg)
{
	char buff[FRAME_LEN] = {0};
	char j = 0;
	char k = 0;
    unsigned long i;
	unsigned long curlen = fileLen;
	printf("[%s] running, Thread ID = %lu\n",__FUNCTION__, pthread_self());
	while(1)
	{
		
		for(i = 0;i < fileLen ;i++,j++)
		{
			

			buff[j] = file_buffer[i];
			curlen--;
			printf("%02x ",(unsigned char)file_buffer[i]);
			if(curlen == 0 && (j <= ( FRAME_LEN - 2)))
			{
				printf("\n curlen=%ld\n",curlen);
				cur_checksum = checkSumFun(buff,j+1);
				buff[j+1] = cur_checksum;
				usleep(1000);
				write(fd,buff,j+2);
				usleep(1000);
				while(1)
				{
					if(read_finish)
						break;
					if(resend_flag)
					{
						write(fd,buff,j+2);
						resend_flag = 0;
					}
				}
				read_finish = 0;
				memset(buff,0,FRAME_LEN);
				j = -1;
				goto end;
			}

			if(j == (FRAME_LEN - 2))
			{
				printf("\n curlen=%ld\n",curlen);
				cur_checksum = checkSumFun(buff,FRAME_LEN-1);
				buff[FRAME_LEN-1] = cur_checksum;
				usleep(1000);
				write(fd,buff,FRAME_LEN);
				usleep(1000);
				while(1)
				{
					if(read_finish)
						break;
					if(resend_flag)
					{
						write(fd,buff,FRAME_LEN);
						resend_flag = 0;
					}
				}
				read_finish = 0;
				memset(buff,0,FRAME_LEN);
				j = -1;
			}


		}

		
		sleep(1);
	}
	return NULL;
end:
	exit_flag = 1;
	return NULL;
}

int main(int argc, char **argv)
{
	pthread_t rtid;
	pthread_t wtid;
	int i;

	if (argc < 2) {
		printf("pls make sure dev name and file name\n");
		exit(1);
	}

	dev_name = argv[1];
	file_name = argv[2];
	printf("dev_name:%s, file_name:%s\n", dev_name, file_name);

	file = fopen(file_name, "rb");
	if (!file)
	{
	    fprintf(stderr, "can't open file \n");
	    exit(1);
	}

	fseek(file, 0, SEEK_END);
	fileLen=ftell(file);
	fseek(file, 0, SEEK_SET);

	printf("fileLen==%ld\n",fileLen);
	file_buffer=(char *)malloc(fileLen+1);

	if (!file_buffer)
	{
		fprintf(stderr, "Memory error!");
		fclose(file);
		exit(1);
	}

	fread(file_buffer, 1, fileLen+1, file);
	fclose(file);

    if((fd=open_port(fd))<0)
    {
        perror("open_port error");
        return;
    }
    if((i=set_opt(fd,115200,8,'N',1))<0)
    {
        perror("set_opt error");
        return;
    }
    printf("fd=%d\n",fd);

	pthread_create(&rtid, NULL, read_thread, NULL);
	pthread_create(&wtid, NULL, write_thread, NULL);

	while(1)
	{
		if(exit_flag)
			break;
	}

	free(file_buffer);
    close(fd);

	exit(1);
    return;
}
